package com.yubest.demo.advice;

import com.fasterxml.jackson.core.type.TypeReference;
import com.yubest.demo.anno.SignProcess;
import com.yubest.demo.util.JsonUtil;
import com.yubest.demo.util.SignUtil;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * @Author hweiyu
 * @Description
 * @Date 2021/3/10 16:51
 */
@ControllerAdvice
public class MyRequestBodyAdvice implements RequestBodyAdvice {

    @Value("${signKey:}")
    private String secret;

    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        SignProcess process = methodParameter.getMethodAnnotation(SignProcess.class);
        //如果带有注解且标记为验签，测进行验签操作
        return null != process && process.verify();
    }

    /**
     * @param httpInputMessage
     * @param methodParameter
     * @param type
     * @param aClass
     * @return
     * @throws IOException
     */
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        HttpHeaders headers = httpInputMessage.getHeaders();
        //源请求参数
        String bodyStr = StreamUtils.copyToString(httpInputMessage.getBody(), Charset.forName("utf-8"));
        //转换成TreeMap结构
        TreeMap<String, String> map = JsonUtil.parse(bodyStr, new TypeReference<TreeMap<String, String>>() {});
        //校验签名
        SignUtil.verify(map, secret);
        Map<String, Object> out = new HashMap<>();
        for (Map.Entry<String, String> entry : map.entrySet()) {
            out.put(entry.getKey(), JsonUtil.read(entry.getValue()));
        }
        String outStr = JsonUtil.toStr(out);
        return new MyHttpInputMessage(headers, outStr.getBytes(Charset.forName("utf-8")));
    }

    @Override
    public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return o;
    }

    @Override
    public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return o;
    }

    /**
     * 自定义消息体，因为org.springframework.http.HttpInputMessage#getBody()只能调一次，所以要重新封装一个可重复读的消息体
     */
    @AllArgsConstructor
    public static class MyHttpInputMessage implements HttpInputMessage {

        private HttpHeaders headers;

        private byte[] body;

        @Override
        public InputStream getBody() throws IOException {
            return new ByteArrayInputStream(body);
        }

        @Override
        public HttpHeaders getHeaders() {
            return headers;
        }
    }

}
